//This is the main display program for the differencces in selected files
//It creates a windows form with two rich text boxes to display the files


#define _WIN32_WINNT 0x0500
#using <System.dll>
#using <System.Windows.Forms.dll>
#using <System.Drawing.dll>
#using <mscorlib.dll>
#include <vector>
#include <string>
#include <utility>
#include <fstream>
#include <iostream>
#include <windows.h>
#include <direct.h>
#include <msclr/marshal.h>
#include "Highlight.cpp"
#include "CompareFile.cpp"
#include "Percent.cpp"
#include "Isolate.cpp"
using namespace std;
using namespace System;
using namespace System::Drawing;
using namespace System::Windows::Forms;
using namespace System::IO;
using namespace System::Runtime::InteropServices;

//global variables
string file1;
string file2;
int display;
int output;
int type;
bool error;

//Adds extra slashes for file paths
string string_replace(string s)
{
	string newString;
	for(unsigned int i = 0; i < s.size(); i++){
		newString+=s[i];
		if(s[i] == '\\')
			newString+='\\';
	}
	return newString;
}

//Returns a character based on the type of file
char dir_info(string filename)
{
	DWORD dwAttr = GetFileAttributes(filename.c_str());
	if(dwAttr == 0xffffffff)
		return 'e';
	else
	{
	if(dwAttr & FILE_ATTRIBUTE_DIRECTORY)
	{
		if (dwAttr & FILE_ATTRIBUTE_ARCHIVE || dwAttr & FILE_ATTRIBUTE_COMPRESSED || dwAttr & FILE_ATTRIBUTE_ENCRYPTED || 
			dwAttr & FILE_ATTRIBUTE_HIDDEN || dwAttr & FILE_ATTRIBUTE_SYSTEM)
			return 'e';
		else
			return 'd';
	}
	else
		return 'f';
	}
}

//Returns the intersecting elements of two vectors
vector<string> intersection(vector<string> files1, vector<string> files2)
{
	vector<string> common;
	unsigned int i, j;

	for( i = 1; i < files1.size(); i++)
	{
		for(j = 1; j < files2.size(); j++)
		{
			if (!strcmp(files1[i].c_str(), files2[j].c_str())){
				common.push_back(files1[i]);
				break;
			}
		}
	}
	return common;
}

//Explodes a string based on the given separator and returns a vector on strings
void StringExplode(string str, string separator, vector<string>* results)
{
    int found;
    found = str.find_first_of(separator);
    while(found != string::npos){
        if(found > 0){
            results->push_back(str.substr(0,found));
        }
        str = str.substr(found+1);
        found = str.find_first_of(separator);
    }
    if(str.length() > 0){
        results->push_back(str);
    }
}
//Creates the new filename for newly created rtf documents 
string makeRtf(string filename, int num)
{
	vector<string> a;
	vector<string> b;

	StringExplode(filename, "\\", &a);
	StringExplode(a.back(), ".", &b);

	string newa;
	string newb;

	char id;
	itoa(num, &id, 10);

	return b[b.size()-2]+"("+id+")"+".rtf";
}

string getFilename(string file){
	vector<string> a;
	vector<string> b;
	StringExplode(file, ".", &a);
	StringExplode(a.front(), "(", &b);
	return b.front();
}

//GUI class
public ref class DifferenceBrowser: public System::Windows::Forms::Form
{
private:
   RichTextBox^ richTextBox1;
   RichTextBox^ richTextBox2;
   MainMenu^ mainMenu1;
   MenuItem^ fileMenuItem;
   MenuItem^ prevMenuItem;
   MenuItem^ folderMenuItem;
   MenuItem^ nextMenuItem;
   Label^ label1;
   Label^ label2;
   String^ prevFileName;
   String^ folderName;
   array<String^,1>^ file1Paths;
   array<String^,1>^ file2Paths;
   array<String^,1>^ display1Paths;
   array<String^,1>^ display2Paths;
   array<String^,1>^ percentages;
   String^ original_dir;
   String^ output_dir;
   int counter;
   int current;
   int compareType;
   int Output;
   int displayType;
   int comparedFiles;

public:

   // Constructor.
   DifferenceBrowser()
   {
	  counter = 0;
	  file1Paths = gcnew array<String^,1>(30);
	  file2Paths = gcnew array<String^,1>(30);
	  display1Paths = gcnew array<String^,1>(30);
	  display2Paths = gcnew array<String^,1>(30);
	  percentages = gcnew array<String^,1>(30);

	  this->label1 = gcnew Label;
      this->mainMenu1 = gcnew System::Windows::Forms::MainMenu;
      this->fileMenuItem = gcnew System::Windows::Forms::MenuItem;
      this->prevMenuItem = gcnew System::Windows::Forms::MenuItem;
      this->folderMenuItem = gcnew System::Windows::Forms::MenuItem;
      this->nextMenuItem = gcnew System::Windows::Forms::MenuItem;
      this->richTextBox1 = gcnew System::Windows::Forms::RichTextBox;
	  this->richTextBox2 = gcnew System::Windows::Forms::RichTextBox;

	  this->mainMenu1->MenuItems->Add( this->fileMenuItem );
      array<System::Windows::Forms::MenuItem^>^temp0 = {this->prevMenuItem,this->nextMenuItem};
      this->fileMenuItem->MenuItems->AddRange( temp0 );
      this->fileMenuItem->Text = "File";
      this->prevMenuItem->Text = "Previous";
      this->prevMenuItem->Enabled = false;
      this->nextMenuItem->Text = "Next";
	  this->nextMenuItem->Click += gcnew System::EventHandler( this, &DifferenceBrowser::nextMenuItem_Click );
	  this->prevMenuItem->Click += gcnew System::EventHandler( this, &DifferenceBrowser::prevMenuItem_Click );
	  this->Closing+= gcnew System::ComponentModel::CancelEventHandler( this, &DifferenceBrowser::on_close );

	  this->label1->Location = System::Drawing::Point( 8, 5 );
	  this->label1->TextAlign = ContentAlignment::MiddleCenter;
	  this->Controls->Add(label1);

      this->richTextBox1->Location = System::Drawing::Point( 10, 50 );
	  this->richTextBox2->Location = System::Drawing::Point(505, 50 );
      this->richTextBox1->Size = System::Drawing::Size( 485, 735 );
	  this->richTextBox2->Size = System::Drawing::Size( 485, 735 );
      this->richTextBox1->Anchor = static_cast<AnchorStyles>(AnchorStyles::Top | AnchorStyles::Left);
      this->richTextBox2->Anchor = static_cast<AnchorStyles>(AnchorStyles::Top); 

	  this->Menu = this->mainMenu1;
      this->Text = "CompareThis Results";
	  this->comparedFiles = 0;

	  char path[100];
	  _getcwd(path, sizeof(path));
	  string fullPath;

	  fullPath.append(path).append("\\output");

	  original_dir = gcnew String(fullPath.c_str());
	  output_dir = gcnew String(fullPath.c_str());
	  string dir = (const char*)System::Runtime::InteropServices::Marshal::StringToHGlobalAnsi(this->original_dir).ToPointer();

	  //Remove previous output directory if necessary
	  if(Directory::Exists(this->output_dir))
		Directory::Delete(this->output_dir, true);

	   mkdir (fullPath.c_str());


	   this->displayType = display;
	   this->Output = output;
	   this->compareType = type;
	   file1 = string_replace(file1).c_str();
	   file1 = file1.substr(0,file1.size());
	   String^ temp1 = gcnew String(file1.c_str());
	   this->file1Paths[0] = temp1;

	   file2 = string_replace(file2).c_str();
	   file2 = file2.substr(0,file2.size());
	   String^ temp2 = gcnew String(file2.c_str());
	   this->file2Paths[0] = temp2;

	  int fileCount=0;
	  dir_scan(fileCount, counter, fullPath.c_str(), 0);
	  this->current = 0;

	  comparedFiles = fileCount;

	  if(fileCount == 0 || this->compareType > 6 || error){
		  this->label1->Size = System::Drawing::Size(250, 40);
		  this->label1->Text="Sorry there was an error. Please try again.";
	  	  this->ClientSize = System::Drawing::Size( 250, 40);
	  }
	  else{
		  this->nextMenuItem->Enabled = true;
		  if(Output != 1){
			  if(this->displayType != 3){
				  this->label1->Size = System::Drawing::Size(980, 40);
				  this->ClientSize = System::Drawing::Size(1000, 800);
				  this->Controls->Add( this->richTextBox1 );
				  this->Controls->Add( this->richTextBox2 );
				  this->richTextBox1->LoadFile(this->display1Paths[0]);
				  this->richTextBox2->LoadFile(this->display2Paths[0]);
			  }
			  else
			  {
				  this->label1->Size = System::Drawing::Size(400, 40);
				  this->ClientSize = System::Drawing::Size( 416, 60 );
			  }
			  vector<string> results1;
			  vector<string> results2;
			  StringExplode((const char*)System::Runtime::InteropServices::Marshal::StringToHGlobalAnsi(display1Paths[0]).ToPointer(), "\\", &results1);
			  this->label1->Text="Comparing " + gcnew String(getFilename(results1.back()).c_str()) +" to ";
			  StringExplode((const char*)System::Runtime::InteropServices::Marshal::StringToHGlobalAnsi(display2Paths[0]).ToPointer(), "\\", &results2);
			  this->label1->Text+= gcnew String(getFilename(results2.back()).c_str());
			  this->label1->Text+="\n"+this->percentages[0];
		  }
		  else
		  {
			this->label1->Text+="Processing is complete. Files are in the output folder";
			this->label1->Size = System::Drawing::Size(300, 50);
			this->ClientSize = System::Drawing::Size( 300, 200 );
		  }

	  }

	  if(comparedFiles < 2)
		  this->nextMenuItem->Enabled = false;
   }

private:
	void on_close(System::Object ^,System::ComponentModel::CancelEventArgs ^){
		if(this->displayType == 1)
			Directory::Delete(this->output_dir, true);
	}

   //Show previous set of compared files if they exist
   //inaccessible otherwise
   void prevMenuItem_Click( Object^ /*sender*/, System::EventArgs^ /*e*/ )
   {
	  current--;
  	  if(this->Output != 1){
		  if(this->displayType != 3)
		  {
			  this->richTextBox1->LoadFile(display1Paths[current]);
			  this->richTextBox2->LoadFile(display2Paths[current]);
			  vector<string> results1;
			  vector<string> results2;
			  StringExplode((const char*)System::Runtime::InteropServices::Marshal::StringToHGlobalAnsi(display1Paths[current]).ToPointer(), "\\", &results1);
			  this->label1->Text="Comparing " + gcnew String(getFilename(results1.back()).c_str()) +" to ";
			  StringExplode((const char*)System::Runtime::InteropServices::Marshal::StringToHGlobalAnsi(display2Paths[current]).ToPointer(), "\\", &results2);
			  this->label1->Text+= gcnew String(getFilename(results2.back()).c_str());
		  }	  
		  this->label1->Text+="\n"+this->percentages[current];
	  }
	  this->label1->Text+="\n"+this->percentages[current];
	  this->nextMenuItem->Enabled = true;
	  if (current == 0)
		this->prevMenuItem->Enabled = false;
   }


   //Show next set of compared files if they exist
   //inaccessible otherwise
   void nextMenuItem_Click( Object^ /*sender*/, System::EventArgs^ /*e*/ )
   {
	  current++;
	  if(this->Output != 1){
		  if(this->displayType != 3){
			  this->richTextBox1->LoadFile(this->display1Paths[current]);
			  this->richTextBox2->LoadFile(this->display2Paths[current]);
			  vector<string> results1;
			  vector<string> results2;
			  StringExplode((const char*)System::Runtime::InteropServices::Marshal::StringToHGlobalAnsi(display1Paths[current]).ToPointer(), "\\", &results1);
			  this->label1->Text="Comparing " + gcnew String(getFilename(results1.back()).c_str()) +" to ";
			  StringExplode((const char*)System::Runtime::InteropServices::Marshal::StringToHGlobalAnsi(display2Paths[current]).ToPointer(), "\\", &results2);
			  this->label1->Text+= gcnew String(getFilename(results2.back()).c_str());
		  }
		  this->label1->Text+="\n"+this->percentages[current];
	  }
	  this->label1->Text+="\n"+this->percentages[current];
	  this->prevMenuItem->Enabled = true;
	  if (current == comparedFiles-1)
		this->nextMenuItem->Enabled = false;
   }

void dir_scan(int &fileCount, int counter, string directory, int depth){
	WIN32_FIND_DATA ffd;
	HANDLE hFind;
	vector<string> files1;
	vector<string> files2;
	vector<string> common;

	counter++;
	
	string file1 = (const char*)System::Runtime::InteropServices::Marshal::StringToHGlobalAnsi(this->file1Paths[counter-1]).ToPointer();
	string file2 = (const char*)System::Runtime::InteropServices::Marshal::StringToHGlobalAnsi(this->file2Paths[counter-1]).ToPointer();

	if (dir_info(file1) == 'd' && dir_info(file2) == 'd'){

		if(counter>1)
		{
			vector<string> R;
			string new_dir = (const char*)System::Runtime::InteropServices::Marshal::StringToHGlobalAnsi(this->original_dir).ToPointer();
			_mkdir( (directory).c_str());
		}
		_chdir((const char*)file1.c_str());
		hFind = FindFirstFile((LPCSTR) "*", &ffd);
		while(FindNextFile(hFind, &ffd)) 
		{
			if( ffd.cFileName != ".." && ffd.cFileName != file1){ 
			files1.push_back(ffd.cFileName);}
		}
		_chdir((const char*)file2.c_str());
		hFind = FindFirstFile((LPCSTR) "*", &ffd);
		while(FindNextFile(hFind, &ffd)) 
		{
			files2.push_back(ffd.cFileName);
		}
		FindClose(hFind);
		common = intersection(files1, files2);

		for(unsigned int i = 0; i < common.size(); i++)
		{
			string newFile1;
			string newFile2;
			newFile1.append(file1).append("\\").append(common[i]);
			newFile2.append(file2).append("\\").append(common[i]);

		   newFile1 = newFile1.substr(0,newFile1.size());
		   String^ temp1 = gcnew String(newFile1.c_str());
		   this->file1Paths[counter] = temp1;

		   newFile2 = newFile2.substr(0,newFile2.size());
		   String^ temp2 = gcnew String(newFile2.c_str());
		   this->file2Paths[counter] = temp2;

			dir_scan(fileCount, counter, (directory + "\\"  + common[i]), depth+1);
		}
}

	//If there are both regular files, compare them
	else if (dir_info(file1) == 'f' && dir_info(file2) == 'f')
	{
		pair<string, string> output;
		string textA;
		string textB;
		vector< pair<int, int> > differences;
		differences = compareFile(file1, file2, this->compareType, textA, textB);


		if(this->displayType == 1 || this->displayType == 4)
			output = printHighlighted(differences, textA, textB);
		if(this->displayType == 2 || this->displayType == 5)
			output = printIsolation(differences, textA, textB);
		if(this->displayType == 3 || this->displayType == 4 || this->displayType == 5)
			percentages[fileCount] = gcnew String(printPercent(differences, textA, textB).c_str());

		vector<string> R;
		ofstream file1out;
		ofstream file2out;
		string newFile1;
		string newFile2;
		string new_directory;

		StringExplode(directory, "\\", &R);

		for(int i=0; i< R.size()-1; i++)
		{
			new_directory+= R[i]+"\\";
		}

		if (depth == 0)
			new_directory+= "output\\";

		newFile1 = new_directory+makeRtf(file1, 1);
		newFile2 = new_directory+makeRtf(file2, 2);

		if(this->displayType != 3)
		{
			file1out.open((newFile1).c_str());
			file2out.open((newFile2).c_str());

			file1out << output.first;
			file2out << output.second;


			file1out.close();
			file2out.close();
		}

		newFile1 = newFile1.substr(0, newFile1.size()).c_str();
		String ^filename1 = gcnew String(newFile1.c_str());
		this->display1Paths[fileCount]=filename1;

		newFile2 = newFile2.substr(0, newFile2.size()).c_str();
		String ^filename2 = gcnew String(newFile2.c_str());
		this->display2Paths[fileCount]=filename2;

		fileCount++;
	}
}

};



// The main entry point for the application.

int main(int argc, char* argv[])
{
	if (argc == 6){
		file1 = argv[1];
		file2 = argv[2];
		output = atoi(argv[3]);
		display = atoi(argv[4]);
		type = atoi(argv[5]);
	}

	else
		error = true;
	//HWND hWnd = GetConsoleWindow();
	//ShowWindow( hWnd, SW_HIDE );
    Application::Run( gcnew DifferenceBrowser );
	return 0;
}


